using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System.Text.Json;
using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing;
using Matrix.Configuration.Events;

namespace Matrix.Configuration.Services
{
    /// <summary>
    /// Matrix配置监控器 - 实现IOptionsMonitor模式的热更新
    /// 监控配置变更并自动通知订阅者
    /// </summary>
    public class MatrixConfigurationMonitor : IConfigurationWatcher, IConfigurationChangeNotifier, IDisposable
    {
        private readonly ILogger<MatrixConfigurationMonitor> _logger;
        private readonly IConfiguration _configuration;
        private readonly IDistributedCache _distributedCache;
        private readonly IRepository<ConfigurationItem, Guid> _configRepository;
        private readonly IOptions<ConfigurationOptions> _configurationOptions;
        private readonly IClock _clock;
        private readonly ICurrentTenant _currentTenant;

        private readonly ConcurrentDictionary<string, DateTime> _lastCheckTimes = new();
        private readonly ConcurrentDictionary<string, IDisposable> _changeTokens = new();
        private readonly ConcurrentDictionary<string, List<Action<ConfigurationChangedEventArgs>>> _subscriptions = new();
        private readonly ConcurrentDictionary<string, List<Delegate>> _typedSubscriptions = new();
        private readonly ReaderWriterLockSlim _subscriptionLock = new();

        private Timer? _monitorTimer;
        private volatile bool _isWatching;
        private readonly object _watchingLock = new();

        public event EventHandler<ConfigurationChangedEventArgs>? ConfigurationChanged;

        public bool IsWatching => _isWatching;
        public IReadOnlyList<string> WatchedKeys => _lastCheckTimes.Keys.ToList();

        public ConfigurationSubscriptionStatistics Statistics { get; private set; } = new();

        public MatrixConfigurationMonitor(
            ILogger<MatrixConfigurationMonitor> logger,
            IConfiguration configuration,
            IDistributedCache distributedCache,
            IRepository<ConfigurationItem, Guid> configRepository,
            IOptions<ConfigurationOptions> configurationOptions,
            IClock clock,
            ICurrentTenant currentTenant)
        {
            _logger = logger;
            _configuration = configuration;
            _distributedCache = distributedCache;
            _configRepository = configRepository;
            _configurationOptions = configurationOptions;
            _clock = clock;
            _currentTenant = currentTenant;
        }

        public async Task StartWatchingAsync(CancellationToken cancellationToken = default)
        {
            if (_isWatching)
            {
                _logger.LogWarning("配置监控已在运行中");
                return;
            }

            lock (_watchingLock)
            {
                if (_isWatching) return;

                _isWatching = true;
                var checkInterval = TimeSpan.FromSeconds(_configurationOptions.Value.Monitoring.MonitorIntervalSeconds);
                _monitorTimer = new Timer(DoMonitor, null, TimeSpan.Zero, checkInterval);
            }

            _logger.LogInformation("配置监控已启动，检查间隔: {Interval}秒", _configurationOptions.Value.Monitoring.MonitorIntervalSeconds);

            // 监听配置文件变更
            await SetupConfigurationFileWatcherAsync();
        }

        public async Task StopWatchingAsync()
        {
            if (!_isWatching)
            {
                return;
            }

            lock (_watchingLock)
            {
                if (!_isWatching) return;

                _isWatching = false;
                _monitorTimer?.Dispose();
                _monitorTimer = null;
            }

            // 清理变更令牌
            foreach (var token in _changeTokens.Values)
            {
                token?.Dispose();
            }
            _changeTokens.Clear();

            _logger.LogInformation("配置监控已停止");

            await Task.CompletedTask;
        }

        public void AddWatchKey(string key, string? @namespace = null)
        {
            Check.NotNullOrWhiteSpace(key, nameof(key));

            var watchKey = GetWatchKey(key, @namespace);
            _lastCheckTimes.TryAdd(watchKey, _clock.Now);

            _logger.LogDebug("添加监控键: {WatchKey}", watchKey);
        }

        public void RemoveWatchKey(string key, string? @namespace = null)
        {
            Check.NotNullOrWhiteSpace(key, nameof(key));

            var watchKey = GetWatchKey(key, @namespace);
            _lastCheckTimes.TryRemove(watchKey, out _);

            _logger.LogDebug("移除监控键: {WatchKey}", watchKey);
        }

        public async Task<bool> TriggerCheckAsync()
        {
            _logger.LogDebug("手动触发配置变更检查");
            return await CheckForChangesAsync();
        }

        public IDisposable SubscribeOnChange(string key, string? @namespace, Action<ConfigurationChangedEventArgs> callback)
        {
            Check.NotNullOrWhiteSpace(key, nameof(key));
            Check.NotNull(callback, nameof(callback));

            var watchKey = GetWatchKey(key, @namespace);

            _subscriptionLock.EnterWriteLock();
            try
            {
                if (!_subscriptions.ContainsKey(watchKey))
                {
                    _subscriptions[watchKey] = new List<Action<ConfigurationChangedEventArgs>>();
                }

                _subscriptions[watchKey].Add(callback);
                UpdateStatistics();

                // 添加到监控列表
                AddWatchKey(key, @namespace);
            }
            finally
            {
                _subscriptionLock.ExitWriteLock();
            }

            _logger.LogDebug("订阅配置变更: {WatchKey}", watchKey);

            return new ConfigurationSubscription(() => Unsubscribe(watchKey, callback));
        }

        public IDisposable SubscribeOnChange<T>(string key, string? @namespace, Action<ConfigurationChangedEventArgs<T>> callback)
        {
            Check.NotNullOrWhiteSpace(key, nameof(key));
            Check.NotNull(callback, nameof(callback));

            var watchKey = GetWatchKey(key, @namespace);

            _subscriptionLock.EnterWriteLock();
            try
            {
                if (!_typedSubscriptions.ContainsKey(watchKey))
                {
                    _typedSubscriptions[watchKey] = new List<Delegate>();
                }

                _typedSubscriptions[watchKey].Add(callback);
                UpdateStatistics();

                // 添加到监控列表
                AddWatchKey(key, @namespace);
            }
            finally
            {
                _subscriptionLock.ExitWriteLock();
            }

            _logger.LogDebug("订阅类型化配置变更: {WatchKey}<{Type}>", watchKey, typeof(T).Name);

            return new ConfigurationSubscription(() => UnsubscribeTyped(watchKey, callback));
        }

        public async Task NotifyChangeAsync(ConfigurationChangedEventArgs changeEvent, CancellationToken cancellationToken = default)
        {
            Check.NotNull(changeEvent, nameof(changeEvent));

            _logger.LogDebug("发布配置变更通知: {Key} in {Namespace}", changeEvent.Key, changeEvent.Namespace);

            // 更新统计信息
            Statistics.LastNotificationTime = _clock.Now;
            Statistics.TotalNotifications++;

            // 触发本地事件
            ConfigurationChanged?.Invoke(this, changeEvent);

            // 通知订阅者
            await NotifySubscribersAsync(changeEvent, cancellationToken);

            // 更新最后检查时间
            var watchKey = GetWatchKey(changeEvent.Key, changeEvent.Namespace);
            _lastCheckTimes.AddOrUpdate(watchKey, _clock.Now, (_, _) => _clock.Now);
        }

        public async Task NotifyChangesAsync(IEnumerable<ConfigurationChangedEventArgs> changeEvents, CancellationToken cancellationToken = default)
        {
            Check.NotNull(changeEvents, nameof(changeEvents));

            var tasks = changeEvents.Select(e => NotifyChangeAsync(e, cancellationToken));
            await Task.WhenAll(tasks);
        }

        public async Task<T?> GetCurrentAsync<T>(string key, string? @namespace = null)
        {
            Check.NotNullOrWhiteSpace(key, nameof(key));

            var tenantId = _currentTenant.Id;
            var configItem = await _configRepository.FirstOrDefaultAsync(x =>
                x.Key == key && x.Namespace == (@namespace ?? "default") && x.TenantId == tenantId);

            if (configItem == null)
            {
                return default;
            }

            try
            {
                return JsonSerializer.Deserialize<T>(configItem.Value);
            }
            catch (JsonException)
            {
                _logger.LogWarning("配置值反序列化失败: {Key} = {Value}", key, configItem.Value);
                return default;
            }
        }

        public void ClearAllSubscriptions()
        {
            _subscriptionLock.EnterWriteLock();
            try
            {
                _subscriptions.Clear();
                _typedSubscriptions.Clear();
                UpdateStatistics();

                _logger.LogInformation("已清除所有配置订阅");
            }
            finally
            {
                _subscriptionLock.ExitWriteLock();
            }
        }

        public ConfigurationSubscriptionStatistics GetStatistics()
        {
            return new ConfigurationSubscriptionStatistics
            {
                TotalSubscriptions = Statistics.TotalSubscriptions,
                SubscriptionsByKey = new Dictionary<string, int>(Statistics.SubscriptionsByKey),
                SubscriptionsByNamespace = new Dictionary<string, int>(Statistics.SubscriptionsByNamespace),
                SubscriptionsByType = new Dictionary<Type, int>(Statistics.SubscriptionsByType),
                LastNotificationTime = Statistics.LastNotificationTime,
                TotalNotifications = Statistics.TotalNotifications
            };
        }

        private void DoMonitor(object? state)
        {
            if (!_isWatching) return;

            try
            {
                _ = Task.Run(async () =>
                {
                    await CheckForChangesAsync();
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "配置监控检查时发生错误");
            }
        }

        private async Task<bool> CheckForChangesAsync()
        {
            var hasChanges = false;
            var tenantId = _currentTenant.Id;

            foreach (var watchKey in _lastCheckTimes.Keys.ToList())
            {
                try
                {
                    var (key, @namespace) = ParseWatchKey(watchKey);
                    var lastCheckTime = _lastCheckTimes[watchKey];

                    var configItem = await _configRepository.FirstOrDefaultAsync(x =>
                        x.Key == key &&
                        x.Namespace == @namespace &&
                        x.TenantId == tenantId &&
                        x.LastModificationTime > lastCheckTime);

                    if (configItem != null)
                    {
                        var oldValue = await GetOldValueAsync(key, @namespace, tenantId);
                        var newValue = DeserializeValue(configItem.Value, configItem.DataType);

                        var changeEvent = new ConfigurationChangedEventArgs
                        {
                            Key = key,
                            Namespace = @namespace,
                            OldValue = oldValue,
                            NewValue = newValue,
                            ValueType = GetTypeByName(configItem.DataType),
                            TenantId = tenantId,
                            ChangeTime = configItem.LastModificationTime ?? _clock.Now,
                            Source = ConfigurationChangeSource.Database,
                            ChangedBy = configItem.LastModifierId?.ToString()
                        };

                        await NotifyChangeAsync(changeEvent);
                        hasChanges = true;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "检查配置变更时发生错误: {WatchKey}", watchKey);
                }
            }

            return hasChanges;
        }

        private async Task SetupConfigurationFileWatcherAsync()
        {
            try
            {
                // 监听配置文件变更
                var changeToken = _configuration.GetReloadToken();
                var disposable = changeToken.RegisterChangeCallback(async _ =>
                {
                    _logger.LogInformation("检测到配置文件变更");

                    // 通知配置文件变更
                    var changeEvent = new ConfigurationChangedEventArgs
                    {
                        Key = "*",
                        Namespace = "default",
                        OldValue = null,
                        NewValue = null,
                        ValueType = typeof(object),
                        TenantId = _currentTenant.Id,
                        ChangeTime = _clock.Now,
                        Source = ConfigurationChangeSource.ConfigurationFile,
                        ChangedBy = "System"
                    };

                    await NotifyChangeAsync(changeEvent);

                    // 重新设置监听
                    await SetupConfigurationFileWatcherAsync();
                }, null);

                _changeTokens.TryAdd("config_file", disposable);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "设置配置文件监听时发生错误");
            }
        }

        private async Task NotifySubscribersAsync(ConfigurationChangedEventArgs changeEvent, CancellationToken cancellationToken)
        {
            var watchKey = GetWatchKey(changeEvent.Key, changeEvent.Namespace);

            // 通知普通订阅者
            if (_subscriptions.TryGetValue(watchKey, out var callbacks))
            {
                var tasks = callbacks.Select(async callback =>
                {
                    try
                    {
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            callback(changeEvent);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "执行配置变更回调时发生错误: {WatchKey}", watchKey);
                    }
                });

                await Task.WhenAll(tasks);
            }

            // 通知类型化订阅者
            if (_typedSubscriptions.TryGetValue(watchKey, out var typedCallbacks))
            {
                var tasks = typedCallbacks.Select(async typedCallback =>
                {
                    try
                    {
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            var method = typedCallback.GetType().GetGenericArguments()[0];
                            var genericCallbackType = typeof(ConfigurationChangedEventArgs<>).MakeGenericType(method);
                            var genericChangeArg = ConvertChangeEventArgs(changeEvent, genericCallbackType);

                            typedCallback.DynamicInvoke(genericChangeArg);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "执行类型化配置变更回调时发生错误: {WatchKey}", watchKey);
                    }
                });

                await Task.WhenAll(tasks);
            }
        }

        private static object? ConvertChangeEventArgs(ConfigurationChangedEventArgs source, Type targetType)
        {
            var method = typeof(MatrixConfigurationMonitor)
                .GetMethod(nameof(CreateTypedChangeEventArgs), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
                ?.MakeGenericMethod(targetType.GetGenericArguments()[0]);

            return method?.Invoke(null, new object[] { source });
        }

        private static ConfigurationChangedEventArgs<T> CreateTypedChangeEventArgs<T>(ConfigurationChangedEventArgs source)
        {
            return new ConfigurationChangedEventArgs<T>
            {
                Key = source.Key,
                Namespace = source.Namespace,
                OldValue = source.OldValue is T oldValue ? oldValue : default,
                NewValue = source.NewValue is T newValue ? newValue : default,
                ValueType = typeof(T),
                TenantId = source.TenantId,
                ChangeTime = source.ChangeTime,
                Source = source.Source,
                ChangedBy = source.ChangedBy
            };
        }

        private void Unsubscribe(string watchKey, Action<ConfigurationChangedEventArgs> callback)
        {
            _subscriptionLock.EnterWriteLock();
            try
            {
                if (_subscriptions.TryGetValue(watchKey, out var callbacks))
                {
                    callbacks.Remove(callback);
                    if (callbacks.Count == 0)
                    {
                        _subscriptions.TryRemove(watchKey, out _);
                    }
                }
                UpdateStatistics();
            }
            finally
            {
                _subscriptionLock.ExitWriteLock();
            }

            _logger.LogDebug("取消配置订阅: {WatchKey}", watchKey);
        }

        private void UnsubscribeTyped<T>(string watchKey, Action<ConfigurationChangedEventArgs<T>> callback)
        {
            _subscriptionLock.EnterWriteLock();
            try
            {
                if (_typedSubscriptions.TryGetValue(watchKey, out var callbacks))
                {
                    callbacks.Remove(callback);
                    if (callbacks.Count == 0)
                    {
                        _typedSubscriptions.TryRemove(watchKey, out _);
                    }
                }
                UpdateStatistics();
            }
            finally
            {
                _subscriptionLock.ExitWriteLock();
            }

            _logger.LogDebug("取消类型化配置订阅: {WatchKey}<{Type}>", watchKey, typeof(T).Name);
        }

        private void UpdateStatistics()
        {
            Statistics.TotalSubscriptions = _subscriptions.Values.Sum(list => list.Count) +
                                        _typedSubscriptions.Values.Sum(list => list.Count);

            Statistics.SubscriptionsByKey.Clear();
            Statistics.SubscriptionsByNamespace.Clear();
            Statistics.SubscriptionsByType.Clear();

            foreach (var kvp in _subscriptions)
            {
                var (key, @namespace) = ParseWatchKey(kvp.Key);
                Statistics.SubscriptionsByKey[key] = kvp.Value.Count;
                Statistics.SubscriptionsByNamespace[@namespace] = kvp.Value.Count;
                Statistics.SubscriptionsByType[typeof(object)] = kvp.Value.Count;
            }

            foreach (var kvp in _typedSubscriptions)
            {
                var (key, @namespace) = ParseWatchKey(kvp.Key);
                Statistics.SubscriptionsByKey[key] = Statistics.SubscriptionsByKey.GetValueOrDefault(key, 0) + kvp.Value.Count;
                Statistics.SubscriptionsByNamespace[@namespace] = Statistics.SubscriptionsByNamespace.GetValueOrDefault(@namespace, 0) + kvp.Value.Count;

                foreach (var callback in kvp.Value)
                {
                    var callbackType = callback.GetType().GetGenericArguments()[0];
                    Statistics.SubscriptionsByType[callbackType] = Statistics.SubscriptionsByType.GetValueOrDefault(callbackType, 0) + 1;
                }
            }
        }

        private static string GetWatchKey(string key, string? @namespace)
        {
            return $"{@namespace ?? "default"}:{key}";
        }

        private static (string Key, string Namespace) ParseWatchKey(string watchKey)
        {
            var parts = watchKey.Split(':', 2);
            return parts.Length == 2 ? (parts[1], parts[0]) : (watchKey, "default");
        }

        private async Task<object?> GetOldValueAsync(string key, string @namespace, Guid? tenantId)
        {
            var cacheKey = $"config:{tenantId}:{@namespace}:{key}";
            try
            {
                var cachedValue = await _distributedCache.GetStringAsync(cacheKey);
                return !string.IsNullOrEmpty(cachedValue) ? cachedValue : null;
            }
            catch
            {
                return null;
            }
        }

        private object? DeserializeValue(string serializedValue, string valueType)
        {
            try
            {
                return JsonSerializer.Deserialize(serializedValue, GetTypeByName(valueType));
            }
            catch
            {
                return serializedValue;
            }
        }

        private static Type GetTypeByName(string typeName)
        {
            return typeName switch
            {
                "string" => typeof(string),
                "int" => typeof(int),
                "bool" => typeof(bool),
                "double" => typeof(double),
                "decimal" => typeof(decimal),
                "DateTime" => typeof(DateTime),
                "Guid" => typeof(Guid),
                _ => typeof(object)
            };
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _monitorTimer?.Dispose();

                foreach (var token in _changeTokens.Values)
                {
                    token?.Dispose();
                }
                _changeTokens.Clear();

                _subscriptionLock?.Dispose();
            }
        }
    }

    /// <summary>
    /// 配置订阅 - 实现IDisposable接口
    /// </summary>
    internal class ConfigurationSubscription : IDisposable
    {
        private readonly Action _unsubscribeAction;
        private volatile bool _disposed;

        public ConfigurationSubscription(Action unsubscribeAction)
        {
            _unsubscribeAction = unsubscribeAction ?? throw new ArgumentNullException(nameof(unsubscribeAction));
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _unsubscribeAction();
                _disposed = true;
            }
        }
    }
}